﻿namespace FxCopContrib
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.FxCop.Sdk;
    using System.Text.RegularExpressions;
    using System.Diagnostics;

    /// <summary>
    /// This rule checks for calls to the static Regex methods or calls to the Regex constructor that 
    /// always receive the same static value. By storing Regex in a static readonly variable and applying the 
    /// RegexOptions.Compiled option, better performance can be achieved.
    /// </summary>
    public class ConsiderMakingRegexReadOnlyAndCompiled : RuleBase
    {
        /// <summary>
        /// 
        /// </summary>
        public ConsiderMakingRegexReadOnlyAndCompiled() : base("ConsiderMakingRegexReadOnlyAndCompiled"){}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public override Microsoft.FxCop.Sdk.ProblemCollection  Check(Microsoft.FxCop.Sdk.Member member)
        {
            if (member == null) { throw new ArgumentNullException("member"); }

            if (member.NodeType == NodeType.Field)
            {
                VisitField((Field)member);
            }
            if (member.NodeType == NodeType.InstanceInitializer
                || member.NodeType == NodeType.StaticInitializer)
            {
                VisitMethod((Method)member);
            }
            return this.Problems;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="field"></param>
        public override void  VisitField(Field field)
        {
            if (field == null) { throw new ArgumentNullException("field"); }

            if (field.Type.FullName == typeof(Regex).FullName)
            {
                if (!field.IsStatic)
                {
                    this.Problems.Add(new Problem(GetNamedResolution("UseStaticField"), field, CheckId));
                }

                if (!field.IsInitOnly)
                {
                    this.Problems.Add(new Problem(GetNamedResolution("UseReadonlyField"), field, CheckId));
                }
            }
 	         base.VisitField(field);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assignment"></param>
        public override void VisitAssignmentStatement(AssignmentStatement assignment)
        {
            if (assignment == null) { throw new ArgumentNullException("assignment"); }

            if (assignment.Target.NodeType == NodeType.MemberBinding
                && assignment.Source.NodeType == NodeType.Construct)
            {
                MemberBinding target = (MemberBinding)assignment.Target;
                if (target.BoundMember.NodeType == NodeType.Field)
                {
                    Field field = (Field)target.BoundMember;
                    if (field.IsStatic && field.IsInitOnly)
                    {
                        Construct source = (Construct)assignment.Source;
                        if (source.Type.FullName == typeof(Regex).FullName)
                        {
                            bool isCompiled = false;
                            if (source.Operands.Count == 2
                                && source.Operands[1].NodeType == NodeType.Literal)
                            {
                                RegexOptions options = (RegexOptions)((Literal)source.Operands[1]).Value;
                                if ((options & RegexOptions.Compiled) == RegexOptions.Compiled)
                                {
                                    isCompiled = true;
                                }
                            }

                            if (!isCompiled)
                            {
                                this.Problems.Add(new Problem(GetNamedResolution("UseRegexOptionsCompiled"), source));
                            }
                        }
                    }
                }
            }
            base.VisitAssignmentStatement(assignment);
        }
    }
}
